﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SymbolEditor.Addin.Helper;
using EnvDTE;
using System.CodeDom;
using System.IO;
using System.CodeDom.Compiler;
using System.Windows;
using ESRI.ArcGIS.Client.Symbols;
using System.Reflection;
using VSLangProj;

namespace SymbolEditor.Addin.CodeDom
{
    class StrongSymbolGenerator:IDisposable
    {
        private string LibPath;
        private string XamlName;
        private string SymbolName;
        private string STR_LIBCLASSNAME
        {
            get
            {
                string filename= "StrongSymbolLib";
                if (curlanguage.Equals("CSharp"))
                {
                    return filename + ".cs";
                }
                else
                {
                    return filename + ".vb";
                }
            }
        }
        private string STR_LIBMAINCLASS = "XamlSymbolLib";
        public EnvDTE.Project Project;
        private CodeDOMSerializer provider = null;
        private CodeGeneratorOptions options = null;
        private string defNamespace = string.Empty;
        private static StrongSymbolGenerator _instance = null;
        private int counter = 0;
        private static string curlanguage = "CSharp";
        private string objDir = "obj";


        private StrongSymbolGenerator()
        {
            provider = new CodeDOMSerializer(curlanguage);
            options = new CodeGeneratorOptions();
            options.BracingStyle = "C";
        }

        public static StrongSymbolGenerator GetInstance(string language)
        {
            string lstLanguage = curlanguage;

            curlanguage = transLanguage(language);

            if (_instance == null)
            {
                _instance = new StrongSymbolGenerator();
            }
            else
            {
                if (!lstLanguage.Equals(curlanguage))
                {
                    _instance.Dispose();
                    _instance = null;
                    _instance = new StrongSymbolGenerator();
                }
            }

            return _instance;
        }
            

        private static string transLanguage(string language)
        {
            switch (language)
            {
                case CodeModelLanguageConstants.vsCMLanguageVB: return "VB"; break;
                case CodeModelLanguageConstants.vsCMLanguageCSharp: return "CSharp"; break;
                default: return string.Empty;
            }
        }

        public void InitialGenerator(string LibPath, string XamlName, string SymbolName)
        {
            // TODO: Complete member initialization
            this.LibPath = LibPath;
            this.XamlName = XamlName;
            this.SymbolName = SymbolName;

        }

        internal string GetStrongSymbolPath()
        {
            counter++;
            return generateSymbolAccessCode();
            
            
        }

        private string generateSymbolAccessCode()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(defNamespace);
            sb.Append(".");
            sb.Append(STR_LIBMAINCLASS);
            sb.Append(".");
            sb.Append(XamlName);
            sb.Append(".");
            sb.Append(SymbolName);

            CodeVariableDeclarationStatement variableDeclaration = new CodeVariableDeclarationStatement(
                typeof(Symbol),
                "symbol" + counter.ToString(),
                new CodeVariableReferenceExpression(sb.ToString()));

            sb.Clear();
            using (TextWriter writer = new StringWriter(sb))
            {
                provider.GenerateCodeFromStatement(variableDeclaration, writer, options);
            }
            return sb.ToString();
        }


        internal bool Prepare()
        {
            bool ret = false;
            try
            {
                //StreamWriter writer = null;
                ProjectItem libclsItem = null;
                CodeCompileUnit codeObject = getStrongSymbolCodeCompUnit(Project, STR_LIBCLASSNAME, out libclsItem);
                updateCodeObject(codeObject);

                provider.GenerateCodeFromCompileUnit(codeObject, libclsItem, options);
                //writer.Dispose();
                addReference2Project(Project);
                //get codedom in condition of if exsit or not ,all supported
                //update codedom 
                //persist codedom to the csharp file in the project,if the file is not exsist , to creat it.
                //add dll referenced into the project 
                ret = true;
            }
            catch (Exception ex)
            {

            }
            return ret;
        }

        private void addReference2Project(EnvDTE.Project Project)
        {
            //foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
            //{
            //    try
            //    {
            //        System.Diagnostics.Debug.WriteLine(assembly.FullName + " at " + assembly.Location);

            //    }
            //    catch (Exception ex)
            //    {


            //    }
            //}
            IEnumerable<Assembly> targets = AppDomain.CurrentDomain.GetAssemblies().Where(element => element.FullName.Equals("ESRI.ArcGIS.Client, Version=2.0.0.314, Culture=neutral, PublicKeyToken=29c6dd6e8553d944"));
            Assembly esriCltAssembly = targets.First();
            VSLangProj.VSProject objVSProject = Project.Object as VSLangProj.VSProject;
            objVSProject.References.Add("System.Xaml");
            objVSProject.References.Add("PresentationFramework");
            objVSProject.References.Add("System");
            objVSProject.References.Add("System.Core");
            objVSProject.References.Add("System.Xml");
            try
            {
                objVSProject.References.Add(esriCltAssembly.Location);
            }
            catch (Exception ex)
            {
               
            }
            objVSProject.References.Add("WindowsBase");

        }

        private void updateCodeObject(CodeCompileUnit codeObject)
        {
            System.CodeDom.CodeNamespace codeNamespace = updateNamespace(codeObject);
            updateReferenceAssembly(codeNamespace);
            var mainclass = updateMainClass(codeNamespace);
            var libclass = updateSymbolLibClass(mainclass);
            updateSymbolAtrribute(libclass);
        }

        private void updateSymbolAtrribute(CodeTypeDeclaration libclass)
        {
            CodeMemberProperty symbolProperty = null;
            IEnumerable<CodeTypeMember> targets = libclass.Members.Cast<CodeTypeMember>().
                                               Where(elemt => elemt.Name.Equals(SymbolName) && (elemt is CodeMemberProperty));

            if (targets.Count() <= 0)
            {

                symbolProperty = new CodeMemberProperty();
                symbolProperty.HasGet = true;
                symbolProperty.HasSet = false;
                symbolProperty.Name = SymbolName;
                symbolProperty.Type = new CodeTypeReference("Symbol");
                symbolProperty.Attributes = MemberAttributes.Public | MemberAttributes.Static;
                libclass.Members.Add(symbolProperty);
                CodeIndexerExpression indexerExpression = new CodeIndexerExpression(new CodeVariableReferenceExpression("rd"), new CodePrimitiveExpression(SymbolName));
                CodeCastExpression castExpression = new CodeCastExpression(
                                                                            "Symbol",
                                                                            indexerExpression);
                symbolProperty.GetStatements.Add(new CodeMethodReturnStatement(castExpression));
            }
            /*
             * get
             * {
             *     return rd[symbolname] as Symbol;
             * }
             */


        }

        private CodeTypeDeclaration updateSymbolLibClass(CodeTypeDeclaration mainclass)
        {
            CodeTypeDeclaration libclass = null;
            IEnumerable<CodeTypeDeclaration> targets = mainclass.Members.Cast<CodeTypeDeclaration>().
                                                Where(elemt => elemt.Name.Equals(XamlName));

            if (targets.Count() > 0)
            {
                libclass = targets.First();
            }
            else
            {
                libclass = new CodeTypeDeclaration(XamlName);
                mainclass.Members.Add(libclass);
                initialLibClass(libclass);
            }

            return libclass;
        }

        private void initialLibClass(CodeTypeDeclaration libclass)
        {
            //private static ResourceDictionary rd;
            var field = new CodeMemberField(typeof(ResourceDictionary), "rd");
            libclass.Members.Add(field);
            field.Attributes = MemberAttributes.Private | MemberAttributes.Static;


            var constuctor = new CodeTypeConstructor();
            libclass.Members.Add(constuctor);
            constuctor.Attributes = MemberAttributes.Static;

            //FileStream stream = new FileStream(path, FileMode.Open);
            CodeObjectCreateExpression expCreatFs = new CodeObjectCreateExpression(typeof(FileStream),
                                                                                    new CodePrimitiveExpression(LibPath + "\\" + XamlName + ".xaml"),
                                                                                    new CodeVariableReferenceExpression("FileMode.Open"));
            CodeVariableDeclarationStatement statStream = new CodeVariableDeclarationStatement("FileStream", "stream", expCreatFs);
            //stream.Dispose();
            CodeMethodInvokeExpression expDipStreame = new CodeMethodInvokeExpression(
                                                        new CodeTypeReferenceExpression("stream"),
                                                        "Dispose");
            //System.Windows.Markup.XamlReader.Load(stream);
            CodeMethodInvokeExpression expLoadXaml = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("System.Windows.Markup.XamlReader"),
                                                                                    "Load",
                                                                                    new CodeVariableReferenceExpression("stream"));
            CodeCastExpression castexp = new CodeCastExpression(typeof(ResourceDictionary), expLoadXaml);
            //rd=System.Windows.Markup.XamlReader.Load(stream);
            CodeAssignStatement as1 = new CodeAssignStatement(new CodeVariableReferenceExpression("rd"), castexp);

            //try catch
            CodeTryCatchFinallyStatement try1 = new CodeTryCatchFinallyStatement();
            try1.TryStatements.Add(statStream);
            try1.TryStatements.Add(as1);
            try1.TryStatements.Add(expDipStreame);            
            CodeCatchClause catch1 = new CodeCatchClause("ex", new CodeTypeReference("System.Exception"));
            try1.CatchClauses.Add(catch1);

            constuctor.Statements.Add(try1);

        }


        private CodeTypeDeclaration updateMainClass(System.CodeDom.CodeNamespace codeNamespace)
        {
            CodeTypeDeclaration codeType = null;
            IEnumerable<CodeTypeDeclaration> targets = codeNamespace.Types.Cast<CodeTypeDeclaration>().
                                    Where(elemt => elemt.Name.Equals(STR_LIBMAINCLASS));
            if (targets.Count() <= 0)
            {
                codeType = new CodeTypeDeclaration(STR_LIBMAINCLASS);
                codeNamespace.Types.Add(codeType);
            }
            else
            {
                codeType = targets.First();
            }

            return codeType;
        }


        private System.CodeDom.CodeNamespace updateNamespace(CodeCompileUnit codeObject)
        {
            System.CodeDom.CodeNamespace codeNamespace = null;
            defNamespace = Project.Properties.Item("DefaultNamespace").Value.ToString();
            IEnumerable<System.CodeDom.CodeNamespace> targets = codeObject.Namespaces.Cast<System.CodeDom.CodeNamespace>().
                                    Where(elemt => elemt.Name.Equals(defNamespace));
            if (targets.Count() <= 0)
            {
                codeNamespace = new System.CodeDom.CodeNamespace(defNamespace);
                codeObject.Namespaces.Add(codeNamespace);
            }
            else
            {
                codeNamespace = targets.First();
            }
            return codeNamespace;
        }

        private void updateReferenceAssembly(System.CodeDom.CodeNamespace codeNamespace)
        {
            codeNamespace.Imports.Add(new CodeNamespaceImport("ESRI.ArcGIS.Client.Symbols"));
            codeNamespace.Imports.Add(new CodeNamespaceImport("System.IO"));
            codeNamespace.Imports.Add(new CodeNamespaceImport("System.Windows"));
        }

        private CodeCompileUnit getStrongSymbolCodeCompUnit(EnvDTE.Project Project, string libfilename, out ProjectItem libclsItem)
        {
            CodeCompileUnit codeObject = null;
            libclsItem = AddinHelper.checkDocumentExsist(Project.ProjectItems, STR_LIBCLASSNAME);
            string fullpath = string.Empty;
            if (libclsItem == null)
            {
                /*handler the situation that the file exists in the
                project directory,but not be added into the project*/
                fullpath = (new FileInfo(Project.FullName)).Directory.FullName + "\\" + libfilename;
                if (File.Exists(fullpath))
                {
                    File.Delete(fullpath);
                }

                libclsItem = AddinHelper.AddEmptyDoc2Project(Project.ProjectItems, libfilename);
                //string tmpDir = System.IO.Path.GetTempPath();
                //string tmpCodefile = tmpDir + "\\" + libfilename;
                //if (File.Exists(tmpCodefile))
                //{
                //    File.Delete(tmpCodefile);
                //}
                //FileInfo file = new FileInfo(tmpCodefile);
                //FileStream stream = file.Create();
                //stream.Dispose();

                

                //libclsItem = Project.ProjectItems.AddFromFileCopy(tmpCodefile);

                //File.Delete(tmpCodefile);
            }
            else
            {
                using (AddinHelper.SafeAccessDocument safeADoc = new AddinHelper.SafeAccessDocument(libclsItem))
                {
                    fullpath = libclsItem.Document.FullName;
                }
            }
            
            using (StreamReader reader = new StreamReader(fullpath))
            {
                if (reader.ReadToEnd().Trim().Equals(string.Empty))
                {
                    codeObject = new CodeCompileUnit();
                }
                else
                {
                    using (AddinHelper.SafeAccessDocument safedoc = new AddinHelper.SafeAccessDocument(libclsItem))
                    {
                        codeObject = provider.Parse(libclsItem);
                    }
                }
            }
            //writer = new StreamWriter(fullpath);

            return codeObject;
        }

        private bool libclassIsExsist(string classname)
        {
            return true;
        }



        #region IDisposable 成员

        public void Dispose()
        {
            provider.Dispose();
            provider = null;
            options = null;
        }

        #endregion
    }
}
